package com.sjb.mianshiya.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sjb.mianshiya.mapper.common.ErrorCode;
import com.sjb.mianshiya.exception.BusinessException;
import com.sjb.mianshiya.exception.ThrowUtils;
import com.sjb.mianshiya.model.domain.Question;
import com.sjb.mianshiya.model.domain.QuestionBank;
import com.sjb.mianshiya.model.domain.QuestionBankQuestion;
import com.sjb.mianshiya.model.domain.User;
import com.sjb.mianshiya.model.request.questionBankQuestion.QuestionBankQuestionQueryRequest;
import com.sjb.mianshiya.model.vo.QuestionBankQuestionVO;
import com.sjb.mianshiya.service.QuestionBankQuestionService;
import com.sjb.mianshiya.mapper.QuestionBankQuestionMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author dell
 * @description 针对表【question_bank_question(题库题目)】的数据库操作Service实现
 * @createDate 2024-09-17 15:40:16
 */
@Service
@Slf4j
public class QuestionBankQuestionServiceImpl extends ServiceImpl<QuestionBankQuestionMapper, QuestionBankQuestion>
        implements QuestionBankQuestionService {

    @Autowired
    @Lazy
    private QuestionServiceImpl questionService;

    @Autowired
    private QuestionBankServiceImpl questionBankService;

    ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
            20,50,60, TimeUnit.SECONDS,new LinkedBlockingDeque<>(10000),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    // 用于保存所有批次的 CompletableFuture
    List<CompletableFuture<Void>> futures = new ArrayList<>();


    @Override
    public void validQuestionBankQuestion(QuestionBankQuestion questionBankQuestion, boolean b) {
        ThrowUtils.throwIf(questionBankQuestion == null, ErrorCode.PARAMS_ERROR);

        Long questionId = questionBankQuestion.getQuestionid();
        if (questionId != null) {
            Question question = questionService.getById(questionId);
            ThrowUtils.throwIf(question == null, ErrorCode.PARAMS_ERROR, "题目不存在");
        }

        Long questionBankId = questionBankQuestion.getQuestionbankid();
        if (questionBankId != null) {
            QuestionBank questionBank = questionBankService.getById(questionBankId);
            ThrowUtils.throwIf(questionBank == null, ErrorCode.PARAMS_ERROR, "题库不存在");
        }
    }

    @Override
    public QuestionBankQuestionVO getQuestionBankQuestionVO(QuestionBankQuestion questionBankQuestion, HttpServletRequest request) {
        return null;
    }

    @Override
    public Wrapper<QuestionBankQuestion> getQueryWrapper(QuestionBankQuestionQueryRequest questionBankQuestionQueryRequest) {
        return null;
    }

    @Override
    public Page<QuestionBankQuestionVO> getQuestionBankQuestionVOPage(Page<QuestionBankQuestion> questionBankQuestionPage, HttpServletRequest request) {
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAddQuestionsToBank(List<Long> questionIds, Long questionBankId, User userLoginUser) {
        QuestionBank questionBank = questionBankService.getById(questionBankId);
        ThrowUtils.throwIf(CollUtil.isEmpty(questionIds), ErrorCode.PARAMS_ERROR, "题目列表为空");
        ThrowUtils.throwIf(questionBankId == null || questionBankId <= 0, ErrorCode.PARAMS_ERROR, "题库非法");
        ThrowUtils.throwIf(userLoginUser == null, ErrorCode.NOT_LOGIN);
        ThrowUtils.throwIf(questionBank == null, ErrorCode.PARAMS_ERROR, "题库信息不存在");

        List<Question> questionsList = questionService.listByIds(questionIds);
        List<Long> validateQuestionIdList = questionsList.stream().map(Question::getId).collect(Collectors.toList());
        ThrowUtils.throwIf(CollUtil.isEmpty(validateQuestionIdList), ErrorCode.PARAMS_ERROR, "合法的题目列表不存在");

        // 在插入之前 校验是否都存在
        LambdaQueryWrapper<QuestionBankQuestion> questionLambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class).eq(QuestionBankQuestion::getQuestionbankid, questionBankId)
                .in(QuestionBankQuestion::getQuestionid, validateQuestionIdList);
        List<QuestionBankQuestion> existQuestionList = this.list(questionLambdaQueryWrapper);
        Set<Long> existQuestionIdSet = existQuestionList.stream().map(QuestionBankQuestion::getId).collect(Collectors.toSet());
        // 以存在于题库中的题目Id 不在进行插入

        validateQuestionIdList = validateQuestionIdList.stream().filter(questionId -> {
            return !existQuestionIdSet.contains(questionId);
        }).collect(Collectors.toList());
        ThrowUtils.throwIf(CollUtil.isEmpty(validateQuestionIdList), ErrorCode.PARAMS_ERROR, "所有题目都以存在于题库中");

        int batchSize = 1000;
        int totalQuestionList = validateQuestionIdList.size();
        for (int i = 0; i < totalQuestionList; i += batchSize) {
            // 生成每批次的数据
            List<Long> subList = validateQuestionIdList.subList(i, Math.min(i + batchSize, totalQuestionList));
            List<QuestionBankQuestion> questionBankQuestions = subList.stream().map(questionId -> {
                QuestionBankQuestion questionBankQuestion = new QuestionBankQuestion();
                questionBankQuestion.setQuestionbankid(questionId);
                questionBankQuestion.setQuestionbankid(questionBankId);
                questionBankQuestion.setUserid(userLoginUser.getId());
                return questionBankQuestion;
            }).collect(Collectors.toList());

            QuestionBankQuestionService questionBankQuestionService = (QuestionBankQuestionServiceImpl) AopContext.currentProxy();
            // 异步处理 每批数据添加到CompletableFuture列表
            CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
                questionBankQuestionService.batchAddQuestionsToBankInner(questionBankQuestions);
            }, customExecutor).exceptionally(ex -> {
                log.error("批处理任务执行失败",ex);
                return null;
            });
            futures.add(completableFuture);
        }
        // 等待所有任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        customExecutor.shutdown();
    }

    /**
     * 批量从题库移除题目信息
     *
     * @param questionIds
     * @param questionBankId
     * @param userLoginUser
     */
    @Override
    public void batchRemoveQuestionsFromBank(List<Long> questionIds, Long questionBankId, User userLoginUser) {
        ThrowUtils.throwIf(questionIds == null, ErrorCode.PARAMS_ERROR, "题目列表信息不存在");
        ThrowUtils.throwIf(userLoginUser == null, ErrorCode.NOT_LOGIN, "用户未登录");
        ThrowUtils.throwIf(questionBankId == null || questionBankId <= 0, ErrorCode.PARAMS_ERROR, "题库非法");

        for (Long questionId : questionIds) {
            LambdaQueryWrapper<QuestionBankQuestion> questionLambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .eq(QuestionBankQuestion::getQuestionbankid, questionBankId)
                    .eq(QuestionBankQuestion::getQuestionid, questionId);
            boolean remove = this.remove(questionLambdaQueryWrapper);
            if (!remove) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "批量从题库信息中出错");
            }
        }
    }

    /**
     * 移除题目信息
     *
     * @param questionIds
     */
    @Override
    public void batchDeleteQuestions(List<Long> questionIds) {
        if (CollUtil.isEmpty(questionIds)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "要删除的题目id信息为空");
        }
        for (Long questionId : questionIds) {
            boolean remove = this.removeById(questionId);
            if (!remove) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "删除题目失败");
            }
            LambdaQueryWrapper<QuestionBankQuestion> questionLambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .eq(QuestionBankQuestion::getQuestionid, questionId);
            remove = this.remove(questionLambdaQueryWrapper);
            if (!remove) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "删除题库题目关联失败");
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAddQuestionsToBankInner(List<QuestionBankQuestion> questionBankQuestions) {
        try {
            boolean result = this.saveBatch(questionBankQuestions);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
        } catch (DataIntegrityViolationException e) {
            log.error("数据库唯一键冲突或违反其他完整性约束, 错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目已存在于该题库，无法重复添加");
        } catch (DataAccessException e) {
            log.error("数据库连接问题、事务问题等导致操作失败, 错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "数据库操作失败");
        } catch (Exception e) {
            // 捕获其他异常，做通用处理
            log.error("添加题目到题库时发生未知错误，错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
        }
    }
}




